package utils

import (
	"context"
	"errors"
	"fmt"
	"ksd-social-api/commons/global"
	"runtime/debug"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var (
	routinePanicHandler func(string)
	routineCount        int64
)

var (
	InsertExistError = errors.New("E11000 duplicate key error")
	WriteBrokenError = errors.New("broken pipe")
)

func Go(f func()) {
	atomic.AddInt64(&routineCount, 1)
	go func() {
		defer func() {
			atomic.AddInt64(&routineCount, -1)
			if r := recover(); r != nil {
				global.Log.Error("【Warning】serevr occour error,please attention")
				if routinePanicHandler != nil {
					var stack string
					if !IsContain(r, WriteBrokenError) {
						stack = string(debug.Stack())
					}
					routinePanicHandler(fmt.Sprintf("[Panic] routineCount:%d \n err: %+v\n", routineCount, r) + stack)
				}
			}
		}()
		f()
	}()
}

func GoParam(i int, f func(i int)) {
	atomic.AddInt64(&routineCount, 1)
	go func(i int) {
		defer func() {
			atomic.AddInt64(&routineCount, -1)
			if r := recover(); r != nil {
				global.Log.Error("【Warning】serevr occour error,please attention")
				if routinePanicHandler != nil {
					var stack string
					if !IsContain(r, WriteBrokenError) {
						stack = string(debug.Stack())
					}
					routinePanicHandler(fmt.Sprintf("[Panic] routineCount:%d \n err: %+v\n", routineCount, r) + stack)
				}
			}
		}()
		f(i)
	}(i)
}

// WaitGoQuit 等待go退出，timeoutSec 超时秒数，<=0表示不超时
func WaitGoQuit(timeoutSec int32) {
	for {
		count := atomic.LoadInt64(&routineCount)
		if count == 0 {
			return
		}
		time.Sleep(time.Second)
		if timeoutSec > 0 {
			timeoutSec--
			if timeoutSec == 0 {
				return
			}
		}
	}
}

func GoAndWaitMust(funcs ...func() error) (err error) {
	var wg sync.WaitGroup
	wg.Add(len(funcs))
	ctx, cancel := context.WithCancel(context.Background())
	for i := range funcs {
		go func(ctx context.Context, f func() error) {
			atomic.AddInt64(&routineCount, 1)
			defer wg.Done()
			defer func() {
				atomic.AddInt64(&routineCount, -1)
				if r := recover(); r != nil {
					global.Log.Error("【Warning】serevr occour error,please attention")
					if routinePanicHandler != nil {
						var stack string
						if !IsContain(r, WriteBrokenError) {
							stack = string(debug.Stack())
						}
						routinePanicHandler(fmt.Sprintf("[Panic] routineCount:%d \n err: %+v\n", routineCount, r) + stack)
					}
				}
			}()
			if routineErr := f(); routineErr != nil {
				err = routineErr
				cancel()
			}
		}(ctx, funcs[i])
	}
	wg.Wait()
	return
}

func GoAndWait(funcs ...func()) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
	defer cancel()

	var wg sync.WaitGroup
	wg.Add(len(funcs))
	for i := range funcs {
		go func(f func()) {
			atomic.AddInt64(&routineCount, 1)
			defer wg.Done()
			defer func() {
				atomic.AddInt64(&routineCount, -1)
				if r := recover(); r != nil {
					global.Log.Error("【Warning】serevr occour error,please attention")
					if routinePanicHandler != nil {
						var stack string
						if !IsContain(r, WriteBrokenError) {
							stack = string(debug.Stack())
						}
						routinePanicHandler(fmt.Sprintf("[Panic] routineCount:%d \n err: %+v\n", routineCount, r) + stack)
					}
				}
			}()
			funcs[i]()
		}(funcs[i])
	}

	done := make(chan struct{})
	go func() {
		wg.Wait()
		close(done)
	}()

	select {
	case <-done:
		return
	case <-ctx.Done():
		return
	}
}

func IsEqual(err error, target error) bool {
	if err != nil && target != nil {
		return err.Error() == target.Error()
	}
	return false
}

func IsContain(err interface{}, target error) bool {
	if err != nil && target != nil {
		var origin string
		switch err.(type) {
		case string:
			origin = err.(string)
		case error:
			origin = err.(error).Error()
		}

		return strings.Contains(origin, target.Error())
	}
	return false
}

// go demo -- 并发编程
//func DomainList() {
//	wg := sync.WaitGroup{}
//	wg.Add(4)
//	utils.Go(func() {
//		defer wg.Done()
//	})
//	utils.Go(func() {
//		defer wg.Done()
//	})
//	utils.Go(func() {
//		defer wg.Done()
//	})
//	utils.Go(func() {
//		defer wg.Done()
//	})
//	wg.Wait()
//}
